/*
 * @(#)ParameterGenerator.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.service.supplement;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.nhncorp.usf.core.Constants;
import com.nhncorp.usf.core.UsfDispatcher;
import com.nhncorp.usf.core.config.runtime.ParameterInfo;
import com.nhncorp.usf.core.service.ExecutionException;
import com.nhncorp.usf.core.service.ServiceExecutionContext;
import com.nhncorp.usf.core.util.OgnlUtil;
import com.nhncorp.usf.core.util.StringUtil;

import static com.nhncorp.usf.core.Constants.*;


import ognl.OgnlException;

/**
 * @author Web Platform Development Team.
 */
public class ParameterGenerator implements PreSupplement {
	private Log log = LogFactory.getLog(ParameterGenerator.class);

	/**
	 * @see com.nhncorp.usf.core.service.supplement.PreSupplement#beforeExecute(com.nhncorp.usf.core.service.ServiceExecutionContext)
	 * @param context ServiceExecutionContext
	 * @return boolean
	 * @throws ExecutionException the ExecutionException
	 */
	public boolean beforeExecute(ServiceExecutionContext context) throws ExecutionException {
		List<ParameterInfo> parameterInfos = context.getMethod().getParameterInfos();
		try {
			Map<String, Object> parameters = getParameterValues(parameterInfos, context);
			context.setMethodParameter(parameters);
		} catch (Exception e) {
			throw new ExecutionException(e);
		}
		return true;
	}

	/**
	 * Invoker에 전달할 {@link HashMap} 타입의 ParameterMap을 생성하여 반환.
	 * 
	 * @param parametersInfos List<ParameterInfo>
	 * @param context ServiceExecutionContext
	 * @return Map<String, Object>
	 * @throws Exception the Exception
	 */
	@SuppressWarnings("unchecked")
	protected Map<String, Object> getParameterValues(List<ParameterInfo> parametersInfos, ServiceExecutionContext context) throws Exception {

		Map<String, Object> parameterValues = new HashMap<String, Object>();
		
		if (!parametersInfos.isEmpty()) {
			for (ParameterInfo parameterInfo : parametersInfos) {
				if (parameterInfo.getId().equals(PARAM_ARG_NAME)) {
					parameterValues.putAll((Map<String, Object>)context.getAttribute(UsfDispatcher.REQUEST_PARAM_NAME));
					
					// Context 정보는 parameterMap을 전달 할때만 함께 전달된다.
					addContextObjects(parameterValues,
						(Map<String, Object>)context.getAttribute(Constants.CONTEXT_SCOPE));
				} else {
					Object value = getValue(parameterInfo, context.getAttributes());
					parameterValues.put(parameterInfo.getId(), value);

				}
			}
		}
		
		return parameterValues;
	}

	/**
	 * extract value of parameter from dataMap using OGNL
	 * 
	 * @param parameterInfo ParameterInfo
	 * @param requestDataMap Map<String, Object>
	 * @throws Exception the Exception
	 * @return Object
	 */
	private Object getValue(ParameterInfo parameterInfo, Map<String, Object> requestDataMap) throws Exception {
		// ref may be ognl expression.
		String reference = parameterInfo.getRef();

		Object value = null;
		
		if (StringUtil.isNotEmpty(reference)) {
			if (requestDataMap.containsKey(reference)) {
				value = requestDataMap.get(reference);
			} else {
				// if reference contains expression or accessor of class.
				try {
					value = OgnlUtil.evaluateAndGet(reference, requestDataMap);
				} catch (OgnlException e) {
					log.error("Reference not found");
					log.error("ParameterId[" + parameterInfo.getId() + "]ReferenceId[" + reference + "]");
					throw e;
				}
			}
		}
		// if ref or value of ref is null, default value will be used.
		if (value == null && parameterInfo.getDefaultValue() != null) {
			value = parameterInfo.getDefaultValue();
		}
		
		return value;
	}

	/**
	 * BO에 전달할 Parameter Map에 Context Object를 추가한다.
	 * 
	 * @param invocationParams BO에 전달할 Parameter Map
	 * @param contextObjectMap Context Object로 구성된 Map
	 */
	private void addContextObjects(Map<String, Object> invocationParams, Map<String, Object> contextObjectMap) {
		if (invocationParams == null || contextObjectMap == null) {
			return;
		}
		for (Entry<String, Object> entry : contextObjectMap.entrySet()) {
			invocationParams.put(entry.getKey(), entry.getValue());
		}
	}
}
